package fr.cephb.locustree.svg;

import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import fr.cephb.locustree.ChromInfo;
import fr.cephb.locustree.DefaultURLCreator;
import fr.cephb.locustree.MappedObject;
import fr.cephb.locustree.Organism;
import fr.cephb.locustree.Track;
import fr.cephb.locustree.URLCreator;
import fr.cephb.xml.SVG;
import fr.cephb.xml.XLink;

public class DefaultEntityPainter
	implements EntityPainter
	{
	private URLCreator urlCreator=new DefaultURLCreator();
	private Organism organism;
	private Track track;
	private ChromInfo chromosome;
	private int paneWidth;
	private int chrom_start;
	private int chrom_length;
	private int label_font_size;
	
	@Override
	public Organism getOrganism()
		{
		return organism;
		}
	
	@Override
	public Track getTrack()
		{
		return track;
		}
	
	@Override
	public ChromInfo getChromosome()
		{
		return chromosome;
		}
	
	
	@Override
	public int getPaneWidth()
		{
		return paneWidth;
		}
	
	@Override
	public void setUrlCreator(URLCreator urlCreator)
		{
		this.urlCreator = urlCreator;
		}
	
	@Override
	public URLCreator getURLCreator()
		{
		return this.urlCreator;
		}
	@Override
	public void setOrganism(Organism organism)
		{
		this.organism = organism;
		}
	@Override
	public void setTrack(Track track)
		{
		this.track = track;
		}
	@Override
	public void setChromosome(ChromInfo chromosome)
		{
		this.chromosome = chromosome;
		}
	@Override
	public void setPaneWidth(int paneWidth)
		{
		this.paneWidth = paneWidth;
		}
	@Override
	public void setChromStart(int chromStart)
		{
		chrom_start = chromStart;
		}
	@Override
	public void setChromLength(int chromLength)
		{
		chrom_length = chromLength;
		}
	
	@Override
	public void setFontSize(int fontSize)
		{
		this.label_font_size=fontSize;
		}
	
	
	public String getURLForObject(MappedObject o)
		{
		if(getURLCreator()==null) return null;
		return getURLCreator().createURL(
				getOrganism(),
				getChromosome(),
				getTrack(),
				o);
		}
	
	private int base2pixel(final int genome_pos)
		{
		return (int)(((genome_pos- this.chrom_start)/(double)this.chrom_length)*getPaneWidth());
		}
	
	
	public Element createGene(
			Document dom,
			final MappedXY o,
			int y
			)
		{
		try
			{
			Map<String,Object> map= o.getObject().getJSON();
			if(map==null) return null;
			
			Rectangle r=o.getBounds();
			y+=r.y;
			
			boolean m_rna=false;
			
			//starts
			Object oStarts=map.get("exonStarts"); 
			if(oStarts==null)
				{
				oStarts=map.get("tStarts"); 
				if(oStarts!=null) m_rna=true;
				}
			
			if(oStarts==null || !(oStarts instanceof List<?>)) return null;
			
			
			List<?> tokens=List.class.cast(oStarts);
			int starts[]=new int[tokens.size()];
			for(int i=0;i< tokens.size();++i) starts[i]=Integer.class.cast(tokens.get(i));
			
			//ends
			Object oEnds=map.get(m_rna?"blockSizes":"exonEnds"); 
			if(oEnds==null || !(oEnds instanceof List<?>)) return null;
			tokens= List.class.cast(oEnds);
			int ends[]=new int[tokens.size()];
			for(int i=0;i< tokens.size();++i)
				{
				ends[i]=Integer.class.cast(tokens.get(i));
				}
			if(starts.length!=ends.length) return null;
			if(m_rna)
				{
				for(int i=0;i< tokens.size();++i)
					{
					ends[i]+=starts[i];
					}
				}
			
			
			//
			String strand="";
			Object oStrand=map.get("strand"); 
			if(oStrand!=null && (oStrand instanceof String))
				{
				strand=oStrand.toString();
				}
			
			final int exon_count= starts.length;
			
			List<Rectangle> exons= new ArrayList<Rectangle>(exon_count);
			for(int i=0;i< exon_count;++i)
				{
				if(starts[i]>ends[i]) return null;
				if(ends[i]< this.chrom_start) continue;
				if(starts[i]>= (this.chrom_start+this.chrom_length)) continue;
				int pos1= base2pixel(starts[i]);
				if(pos1<0) pos1=0;
				int pos2= base2pixel(ends[i]);
				if(pos2> getPaneWidth()) pos2=getPaneWidth();
				if(pos1==pos2) ++pos2; 
				Rectangle exon= new Rectangle(
					pos1,y,(pos2-pos1),r.height
					);
				if(!exons.isEmpty())
					{
					Rectangle last= exons.get(exons.size()-1);
					if(last.x+last.width>=exon.x)
						{
						last.width=(exon.x+exon.width)-last.x;
						}
					else
						{
						exons.add(exon);
						}
					}
				else
					{
					exons.add(exon);
					}
				}
			//if(exons.isEmpty()) return null; non, faut tracer la ligne
			
			Element g= dom.createElement("g");
			//transcription Line
			int x1= base2pixel(o.getObject().getStart());
			if(x1<0) x1=0;
			int x2= base2pixel(o.getObject().getEnd());
			if(x2>=getPaneWidth()) x2=getPaneWidth();
			Element line= dom.createElement("line");
			g.appendChild(line);
			line.setAttribute("x1",String.valueOf(x1));
			line.setAttribute("x2",String.valueOf(x2));
			line.setAttribute("y1",String.valueOf(y+r.height/2));
			line.setAttribute("y2",String.valueOf(y+r.height/2));
			line.setAttribute("style","stroke:red;stroke-width:2px;");
			
			for(int i=0;i< exons.size();++i)
				{
				final int margin=5;
				Rectangle exon= exons.get(i);
				Element rect=null;
				if(i==0 && strand.equals("-") && exon.width>=margin)
					{
					rect= dom.createElement("polygon");
					rect.setAttribute("points",
							""+
							(exon.x+margin)+","+y+" "+
							(exon.x+exon.width)+","+y+" "+
							(exon.x+exon.width)+","+(y+r.height)+" "+
							(exon.x+margin)+","+(y+r.height)+" "+
							(exon.x)+","+(y+r.height/2)
							);
					}
				else if(i==exons.size()-1 && strand.equals("+") && exon.width>=margin)
					{
					rect= dom.createElement("polygon");
					rect.setAttribute("points",
						""+
						(exon.x+exon.width-margin)+","+y+" "+
						(exon.x)+","+y+" "+
						(exon.x)+","+(y+r.height)+" "+
						(exon.x+exon.width-margin)+","+(y+r.height)+" "+
						(exon.x+exon.width)+","+(y+r.height/2)
						);
					}
				else
					{
					rect= dom.createElement("rect");
					rect.setAttribute("x",String.valueOf(exon.x));
					rect.setAttribute("y",String.valueOf(exon.y));
					rect.setAttribute("width",String.valueOf(exon.width));
					rect.setAttribute("height",String.valueOf(exon.height));
					}
				
				g.appendChild(rect);
				rect.setAttribute("style","stroke:black;fill:yellow;");
				}
			
			return g;
			}
		catch(Throwable err)
			{
			return null;
			}
		}
	
	public String getClassForObject(MappedXY o)
		{
		return null;
		}
	
	public String getStyleForObject(MappedXY o)
		{
		return null;
		}
	
	@Override
	public Element paintEntity(Document dom,MappedXY o,int y)
		{
		Rectangle bounds= o.getBounds();
		Element rect= createGene(dom,o,y);
		
		int pix_start=base2pixel(o.getObject().getStart());
		if(pix_start<0) pix_start=0;
		int pix_end= base2pixel(o.getObject().getEnd());
		if(pix_end>=getPaneWidth()) pix_end=getPaneWidth();
		
		
		if(rect==null)
			{
			rect=dom.createElementNS(SVG.NS, "rect");
			rect.setAttribute("x", String.valueOf(pix_start));
			rect.setAttribute("y", String.valueOf(y+bounds.y));
			rect.setAttribute("width", String.valueOf(Math.max(1,pix_end-pix_start)));
			rect.setAttribute("height",String.valueOf(bounds.height));
			
			String clazz= getClassForObject(o);
			if(clazz!=null)
				{
				rect.setAttribute("class",clazz);
				}
			
			String style= getStyleForObject(o);
			if(style!=null)
				{
				rect.setAttribute("style",style);
				}
			else if(clazz==null)
				{
				rect.setAttribute("style","fill:url(#hgrad);stroke:black;");
				}
			}
		Element to_be_returned=rect;
		
		
		
		Element g= null;
		Element text= null;
		final int text_y= bounds.y+y+bounds.height-(bounds.height- label_font_size)/2 -2;
		switch(o.getLabelFlag())
			{
			case MappedXY.LABEL_IGNORED:break;
			case MappedXY.LABEL_ON_LEFT:
				{
				text= dom.createElementNS(SVG.NS, "text");
				g= dom.createElementNS(SVG.NS, "g");
				g.appendChild(text);
				text.appendChild(dom.createTextNode(o.getObject().getName()));
				text.setAttribute("x",String.valueOf(pix_start-1));
				text.setAttribute("y",String.valueOf(text_y));
				text.setAttribute("text-anchor","end");
				g.appendChild(rect);
				break;
				}
			case MappedXY.LABEL_ON_RIGHT:
				{
				text= dom.createElementNS(SVG.NS, "text");
				g= dom.createElementNS(SVG.NS, "g");
				g.appendChild(text);
				text.appendChild(dom.createTextNode(o.getObject().getName()));
				text.setAttribute("x",String.valueOf(pix_end+1));
				text.setAttribute("y",String.valueOf(text_y));
				text.setAttribute("text-anchor","start");
				g.appendChild(rect);
				break;
				}
			case MappedXY.LABEL_ON_MID:
				{
				text= dom.createElementNS(SVG.NS, "text");
				g= dom.createElementNS(SVG.NS, "g");
				g.appendChild(rect);
				
				g.appendChild(text);
				text.appendChild(dom.createTextNode(o.getObject().getName()));
				text.setAttribute("x",String.valueOf((pix_end+pix_start)/2));
				text.setAttribute("y",String.valueOf(text_y));
				text.setAttribute("text-anchor","middle");
				break;
				}
			}
		
		if(g!=null)
			{
			to_be_returned=g;
			}
		
		
		String url= getURLForObject(o.getObject());
		if(url!=null)
			{
			Element anchor=dom.createElementNS(SVG.NS, "a");
			anchor.setAttributeNS(XLink.NS,"xlink:href",url);
			anchor.appendChild(to_be_returned);
			to_be_returned=anchor;
			}
		to_be_returned.setAttribute("title",o.getObject().getName());
		//to_be_returned.setAttribute("id",track.getName()+":"+o.getObject().getId());
		return to_be_returned;
		}
	}
